package com.example.springframeworklearning.config;

import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean;
import org.springframework.validation.beanvalidation.MethodValidationPostProcessor;
import org.springframework.validation.beanvalidation.SpringConstraintValidatorFactory;

/**
 * 注意如果要使用 LocalValidatorFactoryBean 需要引入两个依赖
 * jakarta.validation-api 和 spring-boot-starter-validation
 * 详情请见 pom.xml
 * https://stackoverflow.com/questions/26637800/classnotfoundexception-javax-validation-validatorfactory
 *
 * @author luo
 * @since 2022/9/21 14:30
 */
@Configuration
public class ValidatorConfig {


    /**
     * 配置自定义约束
     * 条件：
     * 1、需要 @Constraint 注解
     * 2、一个 javax.validation.ConstraintValidator 的实现类 ，实现校验功能
     * LocalValidatorFactoryBean 实现了javax.validation.ValidatorFactory 、org.springframework.validation.Validator 和 javax.validation.Validator 接口
     * 默认情况下 LocalValidatorFactoryBean 配置了 SpringConstraintValidatorFactory
     * SpringConstraintValidatorFactory 没有实现 ValidatorFactory 接口 而是实现了 ConstraintValidatorFactory 接口
     * SpringConstraintValidatorFactory 使用 spring 来创建 ConstraintValidator 实例
     * @return
     */
    @Bean
    public LocalValidatorFactoryBean localValidatorFactoryBean(AutowireCapableBeanFactory autowireCapableBeanFactory) {
        LocalValidatorFactoryBean result = new LocalValidatorFactoryBean();

        SpringConstraintValidatorFactory springConstraintValidatorFactory = new SpringConstraintValidatorFactory(autowireCapableBeanFactory);

        result.setConstraintValidatorFactory(springConstraintValidatorFactory);

        return result;
    }


    /**
     * spring 驱动的 方法校验
     * 为了能使spring 驱动的校验 有效
     * 所有的目标类必须标注 spring 的 @Validated 注解
     * 方法校验依赖于 aop 代理，要么是 jdk 动态代理 要么是 CGLib 代理
     * 因为底层使用的是代理来控制，所以如果直接通过 . 来访问属性，那么代理是无法进行拦截的
     * 也就无法校验了
     * @return
     */
    @Bean
    public MethodValidationPostProcessor validationPostProcessor() {
        return new MethodValidationPostProcessor();
    }
}
